#include "U2SceneManagerEnumerator.h"

#include "U2Exception.h"
#include "U2LogManager.h"


U2EG_NAMESPACE_USING


//-----------------------------------------------------------------------
template<> U2SceneManagerEnumerator* U2Singleton<U2SceneManagerEnumerator>::s_pSingleton = 0;
U2SceneManagerEnumerator* U2SceneManagerEnumerator::getSingletonPtr(void)
{
    return s_pSingleton;
}
U2SceneManagerEnumerator& U2SceneManagerEnumerator::getSingleton(void)
{  
    assert( s_pSingleton );  return ( *s_pSingleton );  
}

//-----------------------------------------------------------------------
U2SceneManagerEnumerator::U2SceneManagerEnumerator()
	: mInstanceCreateCount(0), mCurrentRenderSystem(0)
{
    addFactory(&mDefaultFactory);
}
//-----------------------------------------------------------------------
U2SceneManagerEnumerator::~U2SceneManagerEnumerator()
{
	// Destroy all remaining instances
	// Really should have shutdown and unregistered by now, but catch here in case
	Instances instancesCopy = mInstances;
	for (Instances::iterator i = instancesCopy.begin(); i != instancesCopy.end(); ++i)
	{
		// destroy instances
		for(Factories::iterator f = mFactories.begin(); f != mFactories.end(); ++f)
		{
			if ((*f)->getMetaData().typeName == i->second->getTypeName())
			{
				(*f)->destroyInstance(i->second);
				mInstances.erase(i->first);
				break;
			}
		}

	}
	mInstances.clear();
}
//-----------------------------------------------------------------------
void U2SceneManagerEnumerator::addFactory(U2SceneManagerFactory* fact)
{
	mFactories.push_back(fact);
	// add to metadata
	mMetaDataList.push_back(&fact->getMetaData());
	// Log
	U2LogManager::getSingleton().logMessage("U2SceneManagerFactory for type '" +
		fact->getMetaData().typeName + "' registered.");
}
//-----------------------------------------------------------------------
void U2SceneManagerEnumerator::removeFactory(U2SceneManagerFactory* fact)
{
	// destroy all instances for this factory
	for (Instances::iterator i = mInstances.begin(); i != mInstances.end(); )
	{
		U2SceneManager* instance = i->second;
		if (instance->getTypeName() == fact->getMetaData().typeName)
		{
			fact->destroyInstance(instance);
			Instances::iterator deli = i++;
			mInstances.erase(deli);
		}
		else
		{
			++i;
		}
	}
	// remove from metadata
	for (MetaDataList::iterator m = mMetaDataList.begin(); m != mMetaDataList.end(); ++m)
	{
		if(*m == &(fact->getMetaData()))
		{
			mMetaDataList.erase(m);
			break;
		}
	}
	mFactories.remove(fact);
}
//-----------------------------------------------------------------------
const SceneManagerMetaData* U2SceneManagerEnumerator::getMetaData(const U2String& typeName) const
{
	for (MetaDataList::const_iterator i = mMetaDataList.begin(); 
		i != mMetaDataList.end(); ++i)
	{
		if (typeName == (*i)->typeName)
		{
			return *i;
		}
	}

    U2_EXCEPT(U2Exception::ERR_ITEM_NOT_FOUND, 
	    "No metadata found for scene manager of type '" + typeName + "'",
	    "U2SceneManagerEnumerator::createSceneManager");
}
//-----------------------------------------------------------------------
U2SceneManagerEnumerator::MetaDataIterator U2SceneManagerEnumerator::getMetaDataIterator(void) const
{
	return MetaDataIterator(mMetaDataList.begin(), mMetaDataList.end());
}
//-----------------------------------------------------------------------
U2SceneManager* U2SceneManagerEnumerator::createSceneManager(
	const U2String& typeName, const U2String& instanceName)
{
	if (mInstances.find(instanceName) != mInstances.end())
	{
		U2_EXCEPT(U2Exception::ERR_DUPLICATE_ITEM, 
			"U2SceneManager instance called '" + instanceName + "' already exists",
			"U2SceneManagerEnumerator::createSceneManager");
	}

	U2SceneManager* inst = 0;
	for(Factories::iterator i = mFactories.begin(); i != mFactories.end(); ++i)
	{
		if ((*i)->getMetaData().typeName == typeName)
		{
			if (instanceName.empty())
			{
				// generate a name
				U2StringUtil::U2StrStreamType s;
				s << "SceneManagerInstance" << ++mInstanceCreateCount;
				inst = (*i)->createInstance(s.str());
			}
			else
			{
				inst = (*i)->createInstance(instanceName);
			}
			break;
		}
	}

	if (!inst)
	{
		// Error!
		U2_EXCEPT(U2Exception::ERR_ITEM_NOT_FOUND, 
			"No factory found for scene manager of type '" + typeName + "'",
			"U2SceneManagerEnumerator::createSceneManager");
	}

	/// assign rs if already configured
	if (mCurrentRenderSystem)
		inst->_setDestinationRenderSystem(mCurrentRenderSystem);

	mInstances[inst->getName()] = inst;
	
	return inst;
}
//-----------------------------------------------------------------------
U2SceneManager* U2SceneManagerEnumerator::createSceneManager(
	SceneTypeMask typeMask, const U2String& instanceName)
{
	if (mInstances.find(instanceName) != mInstances.end())
	{
		U2_EXCEPT(U2Exception::ERR_DUPLICATE_ITEM, 
			"U2SceneManager instance called '" + instanceName + "' already exists",
			"U2SceneManagerEnumerator::createSceneManager");
	}

	U2SceneManager* inst = 0;
	U2String name = instanceName;
	if (name.empty())
	{
		// generate a name
		U2StringUtil::U2StrStreamType s;
		s << "SceneManagerInstance" << ++mInstanceCreateCount;
		name = s.str();
	}

	// Iterate backwards to find the matching factory registered last
	for(Factories::reverse_iterator i = mFactories.rbegin(); i != mFactories.rend(); ++i)
	{
		if ((*i)->getMetaData().sceneTypeMask & typeMask)
		{
			inst = (*i)->createInstance(name);
			break;
		}
	}

	// use default factory if none
	if (!inst)
		inst = mDefaultFactory.createInstance(name);

	/// assign rs if already configured
	if (mCurrentRenderSystem)
		inst->_setDestinationRenderSystem(mCurrentRenderSystem);
	
	mInstances[inst->getName()] = inst;

	return inst;
}
//-----------------------------------------------------------------------
void U2SceneManagerEnumerator::destroySceneManager(U2SceneManager* sm)
{
	// Erase instance from map
	mInstances.erase(sm->getName());

	// Find factory to destroy
	for(Factories::iterator i = mFactories.begin(); i != mFactories.end(); ++i)
	{
		if ((*i)->getMetaData().typeName == sm->getTypeName())
		{
			(*i)->destroyInstance(sm);
			break;
		}
	}
}
//-----------------------------------------------------------------------
U2SceneManager* U2SceneManagerEnumerator::getSceneManager(const U2String& instanceName) const
{
	Instances::const_iterator i = mInstances.find(instanceName);
	if(i != mInstances.end())
	{
		return i->second;
	}
	else
	{
		U2_EXCEPT(U2Exception::ERR_ITEM_NOT_FOUND, 
			"U2SceneManager instance with name '" + instanceName + "' not found.",
			"U2SceneManagerEnumerator::getSceneManager");
	}
}
//---------------------------------------------------------------------
bool U2SceneManagerEnumerator::hasSceneManager(const U2String& instanceName) const
{
	return mInstances.find(instanceName) != mInstances.end();
}
//-----------------------------------------------------------------------
U2SceneManagerEnumerator::SceneManagerIterator 
U2SceneManagerEnumerator::getSceneManagerIterator(void)
{
	return SceneManagerIterator(mInstances.begin(), mInstances.end());
}
//-----------------------------------------------------------------------
void U2SceneManagerEnumerator::setRenderSystem(U2RenderSystem* rs)
{
	mCurrentRenderSystem = rs;

	for (Instances::iterator i = mInstances.begin(); i != mInstances.end(); ++i)
	{
        i->second->_setDestinationRenderSystem(rs);
    }
}
//-----------------------------------------------------------------------
void U2SceneManagerEnumerator::shutdownAll(void)
{
	for (Instances::iterator i = mInstances.begin(); i != mInstances.end(); ++i)
	{
		// shutdown instances (clear scene)
		i->second->clearScene();			
    }
}


//-----------------------------------------------------------------------
const U2String U2DefaultSceneManagerFactory::FACTORY_TYPE_NAME = "DefaultSceneManager";
//-----------------------------------------------------------------------
void U2DefaultSceneManagerFactory::initMetaData(void) const
{
	mMetaData.typeName = FACTORY_TYPE_NAME;
	mMetaData.description = "The default scene manager";
	mMetaData.sceneTypeMask = ST_GENERIC;
	mMetaData.worldGeometrySupported = false;
}
//-----------------------------------------------------------------------
U2SceneManager* U2DefaultSceneManagerFactory::createInstance(
	const U2String& instanceName)
{
	return U2_NEW U2DefaultSceneManager(instanceName);
}
//-----------------------------------------------------------------------
void U2DefaultSceneManagerFactory::destroyInstance(U2SceneManager* instance)
{
	U2_DELETE instance;
}


//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
U2DefaultSceneManager::U2DefaultSceneManager(const U2String& name)
	: U2SceneManager(name)
{
}
//-----------------------------------------------------------------------
U2DefaultSceneManager::~U2DefaultSceneManager()
{
}
//-----------------------------------------------------------------------
const U2String& U2DefaultSceneManager::getTypeName(void) const
{
	return U2DefaultSceneManagerFactory::FACTORY_TYPE_NAME;
}
